// 控制方块
const g = require('global');

function playNormal(item, iconType, callback) {
    updateIconTypeOnFinished(item, 'normal0'+iconType, callback);
}

function playCancel(item, iconType, callBack) {
    updateIconTypeOnFinished(item, 'cancel0'+iconType, callBack);
}

// 动画播放完成以后
function updateIconTypeOnFinished(item, name, callback) {
    const animState = item.getComponent(cc.Animation);
    if(callback) {
        animState.on('finished', callback, item);
    }
    animState.play(name, callback);
}

// 棋盘控制
class Board {
    constructor(width, height, rows, cols) {
        this.rows = rows;
        this.cols = cols;
        // 按行保存，从上到下
        this.iconTypes = [];
        // 方块的预制体
        this.prefabs = [];
        // 方块位置
        this.pos = [];
        // 被控制的方块
        this.touchBlock = [-1, -1];
        this.deltaPos = cc.v2(-1, -1);
        this.state = g.boardState.ready;
        this.blockWidth = Math.floor(width / this.cols);
        this.blockHeight = Math.floor(height / this.rows);
        this.cancelBlocks = [];
    }

    // 初始化棋盘
    init(iconPrefab, board) {
        const offsetW = Math.floor((this.blockWidth- g.BLOCK_WIDTH) / 2);
        const offsetH = Math.floor((this.blockHeight - g.BLOCK_HEIGHT) / 2);
        // 按行生成，原点在左上角
        for(let row=0; row<this.rows; ++row) {
            this.iconTypes[row] = [];
            this.prefabs[row] = [];
            this.pos[row] = [];
            for(let col=0; col<this.cols; ++col) {
                // 添加预制体
                const item = cc.instantiate(iconPrefab);
                board.addChild(item);
                const x = offsetW+this.blockWidth*col;
                // cocos坐标从左下角开始
                const y = offsetH+this.blockHeight*(this.rows-row-1);
                item.setPosition(x, y);
                this.prefabs[row][col] = item;
                this.pos[row][col] = cc.v2(x, y);
                this.randIconType(row, col);
                playNormal(item, this.iconTypes[row][col]);
            }
        }
        this.printIconTypes();
    }

    // 设置方块类型
    randIconType (i, j) {
        // 相邻块的类型
        const exTypeTable = [-1, -1];
        if(i>1) {
            exTypeTable[0] = this.iconTypes[i-1][j];
        }
        if(j>1) {
            exTypeTable[1] = this.iconTypes[i][j-1];
        }
        const typeTable = [];
        let max = 0;
        for(let i=1; i<g.BLOCK_TYPE+1; ++i) {
            if( i !== exTypeTable[0] && i !== exTypeTable[1]) {
                typeTable[max] = i;
                max += 1;
            }
        }
        this.iconTypes[i][j] = typeTable[g.genRandomInt(1, max)];
    }
    // 获得被触摸的方块
    touchStart(touchLoc) {
        if (this.state !== g.boardState.ready) {
            return
        }
        for(let i=0; i<this.rows; ++i) {
            for (let j=0; j<this.cols; ++j) {
                const touch = this.prefabs[i][j].getBoundingBoxToWorld().contains(touchLoc);
                if (touch) {
                    // 在触摸区域
                    const item = this.prefabs[i][j];
                    this.touchBlock = [i, j];
                    this.deltaPos.x = item.getPosition().x - touchLoc.x;
                    this.deltaPos.y = item.getPosition().y - touchLoc.y;
                    item.zIndex = 1;
                    this.state = g.boardState.moving;
                    return
                }
            }
        }
    }

    // 移动方块
    moving(direct, touchLoc, deltaX, deltaY) {
        if(this.state !== g.boardState.moving) {
            // 本次移动未完成
            return;
        }
        // 判断拖动区域是否出界
        // 第一行不能向下滑，最后一行不能向上滑，第一列不能左滑，最后一列不能右滑
        const [i, j] = this.touchBlock;
        // cc.log(`touch moving, (i,j) = (${i}, ${j}), direction = ${direct}`);
        if(i === 0 && direct === g.direct.down ||
            i === this.rows-1 && direct === g.direct.up ||
            j === 0 && direct === g.direct.left ||
            j === this.cols-1 && direct === g.direct.right
        ) {
            this.prefabs[i][j].setPosition(this.pos[i][j]);
        }
        // 点击到物块自动判断是否可以消除
        if(this.isExchange(direct, deltaX, deltaY)) {
            this.prefabs[i][j].setPosition(this.pos[i][j]);
            this.state = g.boardState.exchange;
            this.exchange(direct);
        } else {
            // 移动物块
            // cc.log('moving block');
            this.prefabs[i][j].setPosition(cc.v2(touchLoc.x+this.deltaPos.x, touchLoc.y+this.deltaPos.y));
        }
    }

    isExchange(direct, deltaX, deltaY) {
        if(direct === g.direct.left || direct === g.direct.right) {
            return deltaX*deltaX > this.blockWidth*this.blockWidth;
        } else {
            return deltaY*deltaY > this.blockHeight*this.blockHeight;
        }
    }

    // 移动结束
    moveEnd(direct, touchLoc) {
        if (this.state === g.boardState.ready) {
            return
        }
        const [i, j] = this.touchBlock;
        // 还原位置
        this.prefabs[i][j].setPosition(this.pos[i][j]);
        if(this.state === g.boardState.exchange) {
            // 已经交换，检查是否有效的移动
            let isValid = this.checkBoard();
            this.playCancelBlocks();
            this.cancelBlocks = [];
            this.genNewBlock(direct);
        }
        if (this.state === g.boardState.exchange){
            // 撤回移动
            this.exchange(direct);
        }
        this.state = g.boardState.ready;
    }
    // 交换图标
    exchange(direct) {
        const [i, j] = this.touchBlock;
        let replaceX = i, replaceY = j;
        let offset = 1;
        // cc.log(`directory = ${direct} on exchange`);
        // 坐标原点在左上角
        if(direct === g.direct.left || direct === g.direct.up) {
            offset = -1;
        }
        if (direct === g.direct.left || direct === g.direct.right) {
            replaceY = j+offset;
        } else {
            replaceX = i+offset;
        }
        // TODO check
        if(replaceX === -1 || replaceY === -1) {
            // 存在空图标，不允许交换
            return
        }
        const originIcon = this.iconTypes[i][j];
        const replaceIcon = this.iconTypes[replaceX][replaceY];
        if(!originIcon || !replaceIcon) {
            return
        }
        this.iconTypes[i][j] = replaceIcon;
        playNormal(this.prefabs[i][j], replaceIcon);
        this.iconTypes[replaceX][replaceY] = originIcon;
        playNormal(this.prefabs[replaceX][replaceY], originIcon);
    }

    // 消除方块
    playCancelBlocks() {
        // 消除方块
        for(const list of this.cancelBlocks) {
            for(const block of list) {
                // 动画播放完以后，消失
                const item = this.prefabs[block.x][block.y];
                const pos = this.pos[0][block.y];
                if (this.iconTypes[block.x][block.y]) {
                    // 移动最上面，等待生成
                    const deactivate = function() {
                        const x = pos.x;
                        const y = pos.y+g.BLOCK_HEIGHT;
                        this.setPosition(x, y);
                        cc.log(`set empty position = (${x}, ${y})`);
                    };
                    playCancel(item, this.iconTypes[block.x][block.y], deactivate);
                    // 消失
                    this.iconTypes[block.x][block.y] = null;
                }
            }
        }
    }

    // 新块
    // TODO support directory
    genNewBlock(direct) {
        // TODO 需要在最后一个方块移动后再检查一次
        const cb = cc.callFunc(function (target) {
            // 再检查一次
            if(this.checkBoard()) {
                cc.log(`check again`);
                this.playCancelBlocks();
                this.cancelBlocks = [];
                this.genNewBlock(direct);
            }
        }, this);

        let sum = 0;
        // let count = [];
        // 默认方向向下，按列筛选, 从最后一行开始查
        for(let col=0; col<this.cols; ++col) {
            // 最大空格
            sum = 0;
            // count = [];
            for(let row=this.rows-1; row>=0; --row) {
                const icon = this.iconTypes[row][col];
                if (!icon) {
                    // 计算消失的块数量
                    sum++;
                }
                // count[row] = sum;
                const nextRow = row+sum < this.rows ?  row+sum : row+sum-this.rows;
                if(sum !== 0 && this.iconTypes[row][col]) {
                    let forward = cc.moveTo(sum*0.3, this.pos[nextRow][col]);
                    // 交换元素
                    const item = this.prefabs[row][col];
                    const empty = this.prefabs[nextRow][col];
                    this.iconTypes[nextRow][col]= this.iconTypes[row][col];
                    this.iconTypes[row][col] = null;
                    this.printIconTypes();
                    cc.log(` row = ${row}, count = ${sum}, move (${row}, ${col}) -> (${nextRow}, ${col}) `);
                    const seq = cc.sequence(forward, cb);
                    item.runAction(seq);
                    // 交换 prefab 位置
                    this.prefabs[nextRow][col] = item;
                    this.prefabs[row][col] = empty;
                    // 生成新块
                    // 从第一行开始往下滑
                    // forward = cc.moveTo(row*2, this.pos[row][col]);
                    cc.log(`move empty block (${nextRow}, ${col}) => (${row}, ${col})`);
                    // empty.active = true;
                    // this.randIconType(row, col);
                    // playNormal(empty, this.iconTypes[row][col]);
                    // const x = this.pos[0][col].x;
                    // const y = this.pos[0][col].y+g.BLOCK_HEIGHT;
                    // empty.setPosition(x,y);
                    // empty.runAction(forward);
                    // this.iconTypes[row][col] = this.iconTypes[row][col];
                    this.printIconTypes();
                }
            }
            // // 生成新的方块
            // for(let row=this.rows-1; row>=0; --row) {
            //     if(!this.iconTypes[row][col]) {
            //         // 生成新块
            //     }
            // }
            // for(let i=sum; i>0; --i) {
            //     const forward = cc.moveTo(i*0.3, this.pos[i][col]);
            //     // // 从第一行开始往下滑
            //     cc.log(`move empty block (${i}, ${col})`);
            //     const item = this.prefabs[i][col];
            //     item.active = true;
            //     // item.runAction(forward);
            // }
        }
        this.printIconTypes();
    }

    // 检查所有块是否可以消除
    checkBoard() {
        // check row
        let num;
        let isValid;
        let cancelBlock;
        for(let row=0; row<this.rows; ++row) {
            num = 1;
            isValid = false;
            const rowBlocks = [];
            cancelBlock = [cc.v2(row, 0)];
            for(let col=0; col<this.cols; ++col) {
                // 相同的icon
                if(col+1 < this.cols && this.iconTypes[row][col] === this.iconTypes[row][col+1] && this.iconTypes[row][col]) {
                    num++;
                    cancelBlock.push(cc.v2(row, col+1));
                    continue
                }
                if(num >= 3) {
                    rowBlocks.push(cancelBlock);
                }
                cancelBlock = [cc.v2(row, col+1)];
                num = 1;
            }
            if(rowBlocks.length > 0) {
                this.cancelBlocks.push(...rowBlocks);
            }
        }
        // count column
        for(let col=0; col<this.cols; ++col) {
            num = 1;
            isValid = false;
            const colBlocks = [];
            cancelBlock = [cc.v2(0, col)];
            for(let row=0; row<this.rows; ++row) {
                // 相同的icon
                if(row+1 < this.rows && this.iconTypes[row][col] === this.iconTypes[row+1][col] && this.iconTypes[row][col]) {
                    num++;
                    cancelBlock.push(cc.v2(row+1, col));
                    continue;
                }
                if(num >= 3) {
                    colBlocks.push(cancelBlock);
                }
                cancelBlock = [cc.v2(row+1, col)];
                num = 1;
            }
            if(colBlocks.length > 0) {
                this.cancelBlocks.push(...colBlocks);
            }
        }
        isValid = this.cancelBlocks.length > 0;
        cc.log(`check board is valid = ${isValid}, cancelBlocks = ${JSON.stringify(this.cancelBlocks)}`);
        return isValid
    }

    printIconTypes() {
        const s = [];
        for(let row=0; row<this.rows; ++row) {
            let ss = `[${this.iconTypes[row][0]}`;
            for(let col=1; col<this.cols; ++col) {
                ss += `,${this.iconTypes[row][col]}`
            }
            s.push(ss+']');
        }
        cc.log(`iconTypes:\n${s.join('\n')}`);
    }

}

module.exports = {
    Board,
};